package com.ruoyi.common.domain.hand;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class PointTypeHandler extends BaseTypeHandler<Point> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Point point, JdbcType jdbcType) throws SQLException {
        String wkt = String.format("POINT(%s %s)", point.getX(), point.getY());
        ps.setString(i, wkt);
    }

    @Override
    public Point getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return parseWkbPoint(rs.getBytes(columnName));
    }

    @Override
    public Point getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return parseWkbPoint(rs.getBytes(columnIndex));
    }

    @Override
    public Point getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return parseWkbPoint(cs.getBytes(columnIndex));
    }

    private Point parseWkbPoint(byte[] wkb) throws SQLException {
        if (wkb == null || wkb.length != 25) {
            throw new SQLException("Invalid WKB for POINT with SRID");
        }

        // SRID部分
        int srid = ((wkb[0] & 0xFF) << 24) | ((wkb[1] & 0xFF) << 16) | ((wkb[2] & 0xFF) << 8) | (wkb[3] & 0xFF);
        System.out.println("SRID: " + srid);

        // 字节序
        int byteOrder = wkb[4] & 0xFF;
        boolean littleEndian = (byteOrder == 1);

        // 类型
        int type = 0;
        if (littleEndian) {
            type = ((wkb[8] & 0xFF) << 24) | ((wkb[7] & 0xFF) << 16) | ((wkb[6] & 0xFF) << 8) | (wkb[5] & 0xFF);
        } else {
            type = ((wkb[5] & 0xFF) << 24) | ((wkb[6] & 0xFF) << 16) | ((wkb[7] & 0xFF) << 8) | (wkb[8] & 0xFF);
        }
        if (type != 1) {
            throw new SQLException("Not a POINT type, type code=" + type);
        }

        // X坐标起始索引 = 9
        double x = readDouble(wkb, 9, littleEndian);
        double y = readDouble(wkb, 17, littleEndian);

        return new Point(x, y);
    }

    private double readDouble(byte[] bytes, int start, boolean littleEndian) {
        long longBits = 0;
        if (littleEndian) {
            for (int i = 7; i >= 0; i--) {
                longBits <<= 8;
                longBits |= (bytes[start + i] & 0xFF);
            }
        } else {
            for (int i = 0; i < 8; i++) {
                longBits <<= 8;
                longBits |= (bytes[start + i] & 0xFF);
            }
        }
        return Double.longBitsToDouble(longBits);
    }
}